package validator

import (
	"encoding/json"
	"fmt"
	"reflect"
	"sort"
	"strings"
	"unicode"

	"gitee.com/iece/common/errorx"

	"github.com/gin-gonic/gin/binding"
	"github.com/go-playground/locales/zh"
	ut "github.com/go-playground/universal-translator"
	"github.com/go-playground/validator/v10"
	zh_translations "github.com/go-playground/validator/v10/translations/zh"
)

type validate struct {
	validate *validator.Validate
	trans    ut.Translator
}

var v *validate

func SetDefault(val *validate) {
	v = val
}

func NewValidate(v *validator.Validate) *validate {
	val := &validate{validate: v}
	cn := zh.New()
	uni := ut.New(cn, cn)
	trans, _ := uni.GetTranslator(cn.Locale())
	val.trans = trans

	val.validate.RegisterTagNameFunc(labelAsTagNameFunc)
	_ = zh_translations.RegisterDefaultTranslations(val.validate, val.trans)
	return val
}

func addValueToMap(fields map[string]string) map[string]interface{} {
	res := make(map[string]interface{})
	for field, err := range fields {
		fieldArr := strings.SplitN(field, ".", 2)
		if len(fieldArr) > 1 {
			newFields := map[string]string{fieldArr[1]: err}
			returnMap := addValueToMap(newFields)
			if res[fieldArr[0]] != nil {
				for k, v := range returnMap {
					res[fieldArr[0]].(map[string]interface{})[k] = v
				}
			} else {
				res[fieldArr[0]] = returnMap
			}
			continue
		} else {
			res[field] = err
			continue
		}
	}
	return res
}

// 去掉结构体名称前缀
func removeTopStruct(fields map[string]string) map[string]interface{} {
	lowerMap := map[string]string{}
	for field, err := range fields {
		fieldArr := strings.SplitN(field, ".", 2)
		f := fieldArr[1]
		f = unTitle(f)
		lowerMap[f] = err
	}
	res := addValueToMap(lowerMap)
	return res
}

func unTitle(s string) string {
	ns := []rune(s)
	if len(ns) != 0 { // 首字母变小写
		ns[0] = unicode.ToLower(ns[0])
	}
	return string(ns)
}

func (v *validate) translateErrs(errs validator.ValidationErrors) string {
	trans := make(validator.ValidationErrorsTranslations)

	var fe validator.FieldError

	for i := 0; i < len(errs); i++ {
		fe = errs[i]

		structNamespace := fe.StructNamespace()
		structNamespace = unTitle(structNamespace)
		trans[structNamespace] = fe.Translate(v.trans)
	}

	// translate := errs.Translate(v.trans)

	errMap := removeTopStruct(trans)
	bytes, _ := json.Marshal(errMap)
	return string(bytes)
}

// 返回一个错误信息
func (v *validate) translateErr(errs validator.ValidationErrors) string {
	translate := errs.Translate(v.trans)
	errMap := removeTopStruct(translate)

	if len(errMap) == 0 {
		return ""
	}
	keys := make([]string, 0, len(errMap))
	for s := range errMap {
		keys = append(keys, s)
	}
	sort.Strings(keys)
	val := errMap[keys[0]]
	if s, ok := val.(string); ok {
		return s
	}
	bytes, _ := json.Marshal(val)
	return string(bytes)
}

// 转换为 weberror.BaseWebError 类型的 ParamError
func (v *validate) Conv2ParmaErr(err error) error {
	if err == nil {
		return nil
	}
	var errStr string
	switch err0 := err.(type) {
	case validator.ValidationErrors:
		errStr = v.translateErr(err0)
	case *json.UnmarshalTypeError:
		errStr = fmt.Errorf("%s 类型错误，期望类型 %s", err0.Field, err0.Type.String()).Error()
	default:
		errStr = err.Error() // unknown error.
	}
	return errorx.NewParamError(errStr)
}

func init() {
	v = NewValidate(binding.Validator.Engine().(*validator.Validate))
}

func labelAsTagNameFunc(field reflect.StructField) string {
	label := field.Tag.Get("label")
	return label
}

// 转换为 ParamError
func Conv2ParmaErr(err error) error {
	return v.Conv2ParmaErr(err)
}

// 校验
func ValidateStruct(param any) (err error) {
	return v.validate.Struct(param)
}
